package voloshyn.paliy.connection;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.util.UUID;

import voloshyn.paliy.utils.MathUtils;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;

import com.motion_detection.public_interface.MotionDetection;

public class VideoStreamingService extends Thread {
	private static final String UUID_STR = "38fa6170-0292-11e2-a21f-0800200c9a66";
	private static String TAG = "VideoStreamingService";
	private ConnectHandler connectHandler = new ConnectHandler();
	private BluetoothSocket cameraSocket = null;
	private BufferedInputStream input = null;
	private BluetoothDevice remoteCamera;
	private boolean isConnected = false;
	private String address;
	private long lastSocketAction;
	private MotionDetection detection = null;
	private Context context;

	public VideoStreamingService(String theCameraAddress, Context context) {
		address = theCameraAddress;
		this.context = context;
	}

	public void setOnConnectListener(OnConnectListener theConnectListener) {
		connectHandler.setOnConnectListener(theConnectListener);
	}

	@Override
	public void run() {
		remoteCamera = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
				address);
		UUID cameraUUID = UUID.fromString(UUID_STR);
		try {
			cameraSocket = remoteCamera
					.createRfcommSocketToServiceRecord(cameraUUID);
			cameraSocket.connect();
			input = new BufferedInputStream(cameraSocket.getInputStream());
			lastSocketAction = System.currentTimeMillis();
			isConnected = true;
			while (isConnected) {
				if (!cameraSocket.isConnected()) {
					throw new IOException("Is disconnected");
				}
				int currentPosition = 0;
				boolean waitForImage = true;
				byte[] info = null;
				while (waitForImage) {
					if (input.available() > 0) {
						lastSocketAction = System.currentTimeMillis();
						int available = input.available();
						if (info == null) {
							if (available >= 4) {
								byte[] lengthBytes = new byte[4];
								input.read(lengthBytes, 0, lengthBytes.length);
								int arrayLength = MathUtils
										.bytesToInt(lengthBytes);
								if (arrayLength < 0) {
									throw new IOException("Negative length");
								}
								info = new byte[arrayLength];
							}
						}

						if (available < info.length - currentPosition) {
							currentPosition += input.read(info,
									currentPosition, available);
						} else {
							currentPosition += input.read(info,
									currentPosition, info.length
											- currentPosition);
						}
						if (currentPosition == info.length) {
							final Bitmap bitmap = BitmapFactory
									.decodeByteArray(info, 0, info.length);
							synchronized (this) {
								if (detection != null) {
									if (detection.isRunning()) {
										detection.putImage(bitmap);
									} else {
										detection.startDetection(bitmap);
									}
								}
							}
							connectHandler
									.handle(ConnectHandler.MESSAGE_INPUT_BITMAP,
											bitmap);
							waitForImage = false;
						}
					} else {
						if (System.currentTimeMillis() - lastSocketAction > 5000) {
							throw new IOException("Socket not active");
						}
					}
				}
			}
		} catch (Throwable e) {
			// e.printStackTrace();
		}
		closeConnection();
		connectHandler.handle(ConnectHandler.MESSAGE_DISCONNECT);
	}

	public void disconnect() {
		isConnected = false;
	}

	private void closeConnection() {
		if (input != null) {
			try {
				input.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		if (cameraSocket != null) {
			try {
				cameraSocket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public boolean isConnected() {
		return isConnected;
	}

	public void setDetection(boolean isChecked, Handler.Callback theCallback) {
		synchronized (this) {
			if (isChecked) {
				detection = new MotionDetection(new Handler(theCallback), context);
			} else {
				if (detection != null) {
					detection.stopDetection();
					detection = null;
				}
			}
		}
	}
}
